{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 本地调用tool工具-使用AppBuilderEventHandler简化tool_call操作\n",
    "\n",
    "基于multi_tool_call的示例，我们可以使用Client应用来执行tool_call操作，完成指定的命令，但是需要自己配置client的思考与运行流程，较为繁琐。SDK提供了使用AppBuilderEventHandler简化tool_call操作的功能\n",
    "\n",
    "##### 配置运行环境&导入Client应用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import appbuilder\n",
    "\n",
    "\n",
    "# AppBuilder Token，替换为您个人的Token\n",
    "os.environ[\"APPBUILDER_TOKEN\"] = \"your api key\"\n",
    "\n",
    "# 应用为：智能问题解决者\n",
    "app_id = \"b9473e78-754b-463a-916b-f0a9097a8e5f\"\n",
    "app_client = appbuilder.AppBuilderClient(app_id)\n",
    "conversation_id = app_client.create_conversation()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 继承AppBuilderEventHandler类实现一个Agent框架的定义"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "from appbuilder.core.console.appbuilder_client.event_handler import AppBuilderEventHandler\n",
    "class MyEventHandler(AppBuilderEventHandler):\n",
    "    def execute_local_command(self, cmd: str):\n",
    "        import subprocess\n",
    "        try:\n",
    "            result = subprocess.check_output(cmd, shell=True).decode(\"utf-8\")\n",
    "            if result.strip() == \"\":\n",
    "                return \"命令执行成功，无返回值\"\n",
    "            return result\n",
    "        except Exception as e:\n",
    "            return str(e)\n",
    "    \n",
    "    def interrupt(self, run_context, run_response):\n",
    "        thought = run_context.current_thought\n",
    "        # 绿色打印\n",
    "        print(\"\\033[1;32m\", \"-> Agent 中间思考: \", thought, \"\\033[0m\")\n",
    "\n",
    "        tool_output = []\n",
    "        for tool_call in run_context.current_tool_calls:\n",
    "            tool_call_id = tool_call.id\n",
    "            tool_res = self.execute_local_command(\n",
    "                **tool_call.function.arguments)\n",
    "            # 蓝色打印\n",
    "            print(\"\\033[1;34m\", \"-> 本地ToolCall结果: \\n\", tool_res, \"\\033[0m\\n\")\n",
    "            tool_output.append(\n",
    "                {\n",
    "                    \"tool_call_id\": tool_call_id,\n",
    "                    \"output\": tool_res\n",
    "                }\n",
    "            )\n",
    "        return tool_output\n",
    "    \n",
    "    def success(self, run_context, run_response):\n",
    "        print(\"\\n\\033[1;31m\",\"-> Agent 非流式回答: \\n\", run_response.answer, \"\\033[0m\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 定义本地的tools工具"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "tools = [\n",
    "    {\n",
    "        \"type\": \"function\",\n",
    "        \"function\": {\n",
    "            \"name\": \"execute_local_command\",\n",
    "            \"description\": \"可以在bash环境中，执行输入的指令，注意，一次只能执行一个原子命令。例如：ls\",\n",
    "            \"parameters\": {\n",
    "                \"type\": \"object\",\n",
    "                \"properties\": {\n",
    "                    \"cmd\": {\n",
    "                        \"type\": \"string\",\n",
    "                        \"description\": \"需要执行的指令\",\n",
    "                    },\n",
    "                },\n",
    "                \"required\": [\"cmd\"],\n",
    "            },\n",
    "        },\n",
    "    }\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 运行Client实现实现链路的调用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with app_client.run_with_handler(\n",
    "        conversation_id = conversation_id,\n",
    "        query = \"请问当前文件夹下有哪些文件？如果没有test.txt文件，请新建一个test.txt文件，内容为：Hello World！\",\n",
    "        tools = tools,\n",
    "        event_handler = MyEventHandler(),\n",
    "    ) as run:\n",
    "        run.until_done()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**output**\n",
    "```\n",
    " -> Agent 中间思考:  首先，我需要使用execute_local_command工具来执行'ls'命令，列出当前文件夹下的所有文件。然后，我需要检查输出中是否存在test.txt文件。如果不存在，我将再次使用execute_local_command工具来执行'echo \"Hello World\" > test.txt'命令，以创建并写入test.txt文件。 \n",
    " -> 本地ToolCall结果: \n",
    " multi_tool_call.ipynb\n",
    "multi_tool_call.py\n",
    "multi_tool_call_with_handler.ipynb\n",
    "multi_tool_call_with_handler.py\n",
    "sdk_ knowledgebase.ipynb\n",
    "sdk_trace.ipynb\n",
    "simple_tool_call.ipynb\n",
    "simple_tool_call.py\n",
    "tmp.log\n",
    "黑神话(悟空).pdf\n",
    " \n",
    "\n",
    " -> Agent 中间思考:  根据execute_local_command工具的返回结果，当前文件夹下并没有test.txt文件。因此，我需要使用execute_local_command工具来执行'echo \"Hello World\" > test.txt'命令，以创建并写入test.txt文件。 \n",
    " -> 本地ToolCall结果: \n",
    " 命令执行成功，无返回值 \n",
    "\n",
    "\n",
    " -> Agent 非流式回答: \n",
    " 当前文件夹下的文件包括：\n",
    "\n",
    "- multi_tool_call.ipynb\n",
    "- multi_tool_call.py\n",
    "- multi_tool_call_with_handler.ipynb\n",
    "...\n",
    "- tmp.log\n",
    "- 黑神话(悟空).pdf\n",
    "\n",
    "经过检查，发现当前文件夹下**不存在**test.txt文件。因此，已经为您新建了一个test.txt文件，并写入了内容“Hello World！”。 \n",
    "```\n",
    "\n",
    "- 使用AppBuilderEventHandler架构可以简化client的交互方式"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "python-12",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
